// src/script/p2pkh.rs

//
// P2PKH（ Pay-To-Public-Key-Hash）
//
// 公钥哈希地址生成：从公钥生成一个哈希地址。
// 锁定脚本（scriptPubKey）：锁定一个输出，使其只能由对应的私钥解锁。
// 解锁脚本（scriptSig）：提供签名和公钥以满足锁定脚本的条件。
// 验证逻辑：验证解锁脚本是否满足锁定脚本的要求。
//
use tracing::trace;

use crate::{
    BlockHeight, CompactSignatureBytes, OpCode, PublicKeyBytes, PublicKeyHashBytes, Script,
};

// Bitcoin core functions:
//  bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CKey& keyRet, int64& nFeeRequiredRet)
//  string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
//  bool CommitTransaction(CWalletTx& wtxNew, const CKey& key)
//  void SetBitcoinAddress(const uint160& hash160) {

// typical P2PKH input script: [signature], [public_key]
pub fn create_tx_input_script(
    payer_sig: CompactSignatureBytes,
    payer_pub_key: PublicKeyBytes,
) -> Script {
    let mut script: Script = Script::new_empty();
    script.push(OpCode::OpSignature(payer_sig));
    script.push(OpCode::OpPubKey(payer_pub_key));
    script
}

// typical P2PKH output script:  OP_DUP, OP_HASH160, [public_key_hash160], OP_EQUALVERIFY, OP_CHECKSIG
#[cfg(test)]
pub fn create_tx_output_script(payee_pub_key: &PublicKeyBytes) -> Script {
    let pub_key_hash = payee_pub_key.to_public_key_hash();
    create_tx_output_script_0(&pub_key_hash)
}

// pub fn create_tx_output_script_1(payee_pub_key_hash: PublicKeyHashBytes) -> Script {
//     // let pub_key_hash = *payee_pub_key_hash.as_bytes_ref();
//     create_tx_output_script_0(payee_pub_key_hash)
// }

pub fn create_tx_output_script_0(payee_pub_key_hash: &PublicKeyHashBytes) -> Script {
    let mut script: Script = Script::new_empty();
    script.push(OpCode::OpDup);
    script.push(OpCode::OpHash160);
    script.push(OpCode::OpPubKeyHash(*payee_pub_key_hash));
    script.push(OpCode::OpEqualverify);
    script.push(OpCode::OpChecksig);
    script
}

pub fn get_transaction_sender(script_signature: &Script) -> Option<PublicKeyHashBytes> {
    trace!("script_signature={}", script_signature);
    for it in script_signature.iter() {
        if let OpCode::OpPubKey(pub_key) = it {
            return Some(pub_key.to_public_key_hash());
        }
    }
    None
}

pub fn get_transaction_receiver(script_pubkey: &Script) -> Option<PublicKeyHashBytes> {
    trace!("script_pubkey={}", script_pubkey);
    script_pubkey.iter().find_map(|op_code| {
        if let OpCode::OpPubKeyHash(pub_key_hash) = op_code {
            trace!("pub_key_hash={}", pub_key_hash);
            Some(*pub_key_hash)
        } else {
            None
        }
    })
}

pub fn get_block_height(script_signature: &Script) -> Option<BlockHeight> {
    for it in script_signature.iter() {
        if let OpCode::OpBlockHeight(h) = it {
            return Some(*h);
        }
    }

    None
}
